/*
 * Copyright (c) 2022 PATEO CONNECT+ (Nanjing) Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "audio_record_manager.h"
#include "voice_assistant_log.h"
#include "common_utils.h"
#include <thread>

using namespace std;
using namespace std::chrono;
using namespace OHOS;
using namespace OHOS::AudioStandard;

namespace OHOS {
namespace CarVoiceAssistant {

    class AudioCapturerCallbackTestImpl : public AudioCapturerCallback {
    public:
        AudioCapturerCallbackTestImpl(wptr<AudioRecordManager> manager)
            : manager_(manager)
        {
        }

        void OnStateChange(const CapturerState state) override
        {
            VOICE_ASSISTANT_LOGI("AudioCapturerCallbackTestImpl:: OnStateChange");
            if (manager_ != nullptr) {
                manager_->OnStateChange(state);
            }
        }

        wptr<AudioRecordManager> manager_;
    };

    AudioRecordManager::AudioRecordManager()
        : callback_(nullptr)
        , status_(AudioRecordStatusNone)
        , recordingTag_(false)
        , audioCapturer_(nullptr)
    {
    }

    AudioRecordManager::~AudioRecordManager()
    {
        if (status_ == AudioRecordStatusRunning) {
            audioCapturer_->Flush();
            audioCapturer_->Stop();
        }
        audioCapturer_->SetCapturerCallback(nullptr);
        audioCapturer_->Release();
        this->callback_ = nullptr;
    }

    void AudioRecordManager::SetCallback(wptr<IAudioRecordCallback> callback)
    {
        callback_ = callback;
    }

    bool AudioRecordManager::StartRecord()
    {
        VOICE_ASSISTANT_LOGI("StartRecord ");

        if (audioCapturer_ == nullptr) {
            VOICE_ASSISTANT_LOGI("AudioCapturer::Create");
            AudioCapturerOptions capturerOptions;
            capturerOptions.streamInfo.samplingRate = static_cast<AudioSamplingRate>(16000);
            capturerOptions.streamInfo.encoding = AudioEncodingType::ENCODING_PCM;
            capturerOptions.streamInfo.format = AudioSampleFormat::SAMPLE_S16LE;
            capturerOptions.streamInfo.channels = AudioChannel::MONO;
            capturerOptions.capturerInfo.sourceType = SourceType::SOURCE_TYPE_MIC;
            capturerOptions.capturerInfo.capturerFlags = 0;

            audioCapturer_ = AudioCapturer::Create(capturerOptions);

            int32_t ret = 0;
            shared_ptr<AudioCapturerCallback> cb1 = make_shared<AudioCapturerCallbackTestImpl>(this);
            ret = audioCapturer_->SetCapturerCallback(cb1);
            if (ret) {
                VOICE_ASSISTANT_LOGI("AudioCapturerTest: SetCapturerCallback failed %d", ret);
                return false;
            }
        }

        int32_t status = audioCapturer_->SetBufferDuration(20);
        if (status) {
            VOICE_ASSISTANT_LOGI("Failed to set buffer duration");
        }

        VOICE_ASSISTANT_LOGI("Starting Stream");
        if (!audioCapturer_->Start()) {
            VOICE_ASSISTANT_LOGI("Start stream failed");
            audioCapturer_->Release();
            audioCapturer_ = nullptr;
            return false;
        }

        recordingTag_ = true;
        status_ = AudioRecordStatusStarting;

        std::thread serviceThread(&AudioRecordManager::StartCapture, this);
        serviceThread.detach();
        return true;
    }

    void AudioRecordManager::StartCapture()
    {
        VOICE_ASSISTANT_LOGI("Capturing started");
        size_t bufferLen;
        if (audioCapturer_->GetBufferSize(bufferLen) < 0) {
            VOICE_ASSISTANT_LOGI(" GetMinimumBufferSize failed");
            return;
        }

        auto buffer = std::make_unique<uint8_t[]>(bufferLen);
        if (buffer == nullptr) {
            VOICE_ASSISTANT_LOGI("AudioCapturerTest: Failed to allocate buffer");
            return;
        }

        VOICE_ASSISTANT_LOGI("AudioPerf Capturer First Frame Read, BUFFER_LEN = %zu", bufferLen);
        while (true) {
            int32_t bytesRead = 0;
            while (bytesRead < bufferLen) {
                if (audioCapturer_ == nullptr) {
                    break;
                }
                int32_t len = audioCapturer_->Read(*(buffer.get() + bytesRead), bufferLen - bytesRead, true);
                if (len >= 0) {
                    bytesRead += len;
                } else {
                    bytesRead = len;
                    break;
                }
            }

            if (!recordingTag_) {
                break;
            }

            if (bytesRead < 0) {
                VOICE_ASSISTANT_LOGI("Bytes read failed. error code %zu", bytesRead);
                break;
            } else if (bytesRead == 0) {
                continue;
            }

            if (callback_ != nullptr) {
                callback_->ReceiveAudioBuffer(buffer.get(), bytesRead);
            }
        }

        VOICE_ASSISTANT_LOGI("Quit loop");
    }

    void AudioRecordManager::StopRecord()
    {
        VOICE_ASSISTANT_LOGI("StopRecord");
        if (audioCapturer_ == nullptr) {
            return;
        }

        recordingTag_ = false;

        if (!audioCapturer_->Flush()) {
            VOICE_ASSISTANT_LOGI("StopRecord: flush failed");
        }

        if (!audioCapturer_->Stop()) {
            VOICE_ASSISTANT_LOGI("StopRecord: Stop failed");
        }

        status_ = AudioRecordStatusNone;
    }

    AudioRecordStatus AudioRecordManager::GetStatus()
    {
        return status_;
    }

    void AudioRecordManager::OnStateChange(const CapturerState state)
    {
        switch (state) {
        case CAPTURER_PREPARED:
            VOICE_ASSISTANT_LOGI("AudioRecordManager: OnStateChange CAPTURER_PREPARED");
            break;
        case CAPTURER_RUNNING:
            VOICE_ASSISTANT_LOGI("AudioRecordManager: OnStateChange CAPTURER_RUNNING");
            status_ = AudioRecordStatusRunning;
            break;
        case CAPTURER_STOPPED:
            VOICE_ASSISTANT_LOGI("AudioRecordManager: OnStateChange CAPTURER_STOPPED");
            status_ = AudioRecordStatusNone;
            recordingTag_ = false;
            break;
        case CAPTURER_RELEASED:
            VOICE_ASSISTANT_LOGI("AudioRecordManager: OnStateChange CAPTURER_RELEASED");
            break;
        default:
            VOICE_ASSISTANT_LOGI("AudioRecordManager: OnStateChange NOT A VALID state");
            break;
        }
    }
}
}
